package formula.web.twork;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.NavigationHandler;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class CustomExceptionHandler extends AbstractExceptionHandlerWrapper {

	private static final String _EXCEPTIONS_SESSION_ATTRIBUTE_NAME = "exceptionBeans";
	private static final String _MAIN_EXCEPTION_DATE_SESSION_ATTRIBUTE_NAME = "errorDate";
	private static final String _ERROR_OUTCOME = "error?faces-redirect=true";
	private static final Logger s_logger = LoggerFactory.getLogger(CustomExceptionHandler.class);

	/**
	 * Crée une nouvelle instance permettant d'établir une façade sur le gestionnaire d'exceptions
	 * précisé
	 * 
	 * @param originalExceptionHandler
	 *            gestionnaire d'exceptions qui sera masqué par cette façade
	 */
	public CustomExceptionHandler(ExceptionHandler originalExceptionHandler) {
		super(originalExceptionHandler);
	}

	/**
	 * Manipule les exceptions levées pour être présentées à l'utilisateur
	 * 
	 * @see javax.faces.context.ExceptionHandlerWrapper#handle()
	 */
	@Override
	public void handle() throws FacesException {
		FacesContext facesContext = FacesContext.getCurrentInstance();
		List<ExceptionBean> exceptionBeans = new ArrayList<ExceptionBean>();
		// handle incoming exceptions
		//
		Iterable<ExceptionQueuedEvent> exceptionQueuedEvent = getUnhandledExceptionQueuedEvents();
		if (exceptionQueuedEvent != null) {
			ExceptionBean currentExceptionBean;
			for (Iterator<ExceptionQueuedEvent> i = exceptionQueuedEvent.iterator(); i.hasNext();) {
				ExceptionQueuedEvent event = i.next();
				if (event != null) {
					currentExceptionBean = new ExceptionBean();
					ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
					Throwable exception = context.getException();
					//
					if (exception != null) {
						s_logger.error("", exception);
						if ((exception.getMessage() != null) && (exception.getMessage().length() > 0)) {
							currentExceptionBean.setMessage(exception.getMessage());
						} else {
							currentExceptionBean.setMessage("[" + exception.getClass().getName() + "]");
						}
						//
						currentExceptionBean.setStackTrace(ExceptionUtils.getStackTrace(exception));
						//
						exceptionBeans.add(currentExceptionBean);
					} // if (exception != null) {
				} // if (event != null) {
					//
				i.remove();
			} // for (Iterator<ExceptionQueuedEvent> i =
				// exceptionQueuedEvent.iterator();
				// i.hasNext();) {
				//
			if (!exceptionBeans.isEmpty()) {
				HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext()
						.getSession(true);

				session.setAttribute(_EXCEPTIONS_SESSION_ATTRIBUTE_NAME, exceptionBeans);
				session.setAttribute(_MAIN_EXCEPTION_DATE_SESSION_ATTRIBUTE_NAME, new Date());

				NavigationHandler navigationHandler = resolveApplication().getNavigationHandler();
				navigationHandler.handleNavigation(facesContext, null, _ERROR_OUTCOME);
				facesContext.renderResponse();
			} // if (!exceptionBeans.isEmpty()) {
		} // if (exceptionQueuedEvent != null) {
			//
			// On donne la main au gestionnaire wrapper afin de poursuivre le
			// traitement de
			// l'exception
			// levée
		getWrapped().handle();
	}

	public Application resolveApplication() {
		Application application = null;
		//
		FacesContext context = FacesContext.getCurrentInstance();
		if (context != null) {
			// logger.debug("Using FacesContext#getCurrentInstance() to resolve current Application");
			application = FacesContext.getCurrentInstance().getApplication();
		} else {
			// logger.debug("FacesContext#currentInstance is null, using FactoryFinder to resolve current Application");
			ApplicationFactory factory = (ApplicationFactory) FactoryFinder
					.getFactory(FactoryFinder.APPLICATION_FACTORY);
			application = factory.getApplication();
		}
		return application;
	}
}
